package io.gitee.kotle.player.ui

import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.media.AudioAttributes
import android.media.AudioFormat
import android.media.AudioTrack
import android.net.Uri
import android.os.Build
import android.text.Spannable
import android.text.SpannableStringBuilder
import android.text.TextPaint
import android.text.method.LinkMovementMethod
import android.text.style.AbsoluteSizeSpan
import android.text.style.BackgroundColorSpan
import android.text.style.CharacterStyle
import android.text.style.ClickableSpan
import android.text.style.ForegroundColorSpan
import android.text.style.ImageSpan
import android.text.style.LineHeightSpan
import android.text.style.StrikethroughSpan
import android.text.style.StyleSpan
import android.text.style.URLSpan
import android.view.Gravity
import android.view.View
import android.widget.TextView
import androidx.annotation.WorkerThread
import com.arthenica.ffmpegkit.FFmpegKit
import com.arthenica.ffmpegkit.FFmpegSession
import com.arthenica.ffmpegkit.FFprobeKit
import java.io.File
import kotlin.math.min

/**
 *
 * 描述：
 *
 * @author Jiale.Wei
 *
 * 创建时间：2023/4/21 10:03
 *
 */
/**
 * 通过变量控制是否隐藏
 */
internal fun View?.setInvisible(visible: Boolean) {
    this ?: return
    if (visible) {
        visible()
    } else {
        invisible()
    }
}

/**
 * view可见
 */
internal fun View?.visible() {
    this ?: return
    if (visibility != View.VISIBLE) {
        visibility = View.VISIBLE
    }
}

/**
 * view隐藏
 */
internal fun View?.invisible() {
    this ?: return
    if (visibility != View.INVISIBLE) {
        visibility = View.INVISIBLE
    }
}


/**
 *
 * 描述：
 *
 * @author jiale.wei
 *
 * 创建时间：2022/8/26 10:24
 *
 */
internal data class SpanBean(
    var text: String?,/*要改变的文本效果*/
    var textColor: Int? = null,/*文字颜色*/
    var textSize: Int? = null,/*文字大小*/
    var textClickListener: Function1<View, Unit>? = null,/*文字点击*/
    var typeface: Int? = null,/*eg:[Typeface.BOLD_ITALIC]*/
    var isBold: Boolean? = null,/*是否加粗*/
    var lineHeight: Int? = null,/*行高*/
    var gravity: Int? = null,/*位置*/
    var url: Pair<String?, View.OnClickListener?>? = null,/*Pair<"https://baidu.com",listener>*/
    var drawable: Drawable? = null,/*设置图片的话，文字将会失效*/
    var isUnderlineText: Boolean? = null,/*是否需要下划线*/
    var underlineColor: Int? = null,/*下划线颜色*/
    var strikethroughSpan: Boolean? = null,/*删除线*/
    var backgroundColor: Int? = null,/*背景色*/
    var widthSpace: Int? = null,/*宽度的间隔*/
    var heightSpace: Int? = null,/*宽度的间隔*/
    var spanWhatList: List<Any>? = null,/*支持其他未被定义的CharacterStyle,或者其他*/
    var textVerticalCenter: Boolean = false,/*文本居中对齐*/
    var maxTextSize: Int? = null,/*最大文本大小*/
)

internal fun TextView?.textFromSpanBean(vararg spanBeans: SpanBean) {
    textFromSpanBean(spanBeans.asList())
}

internal infix fun TextView?.textFromSpanBean(textList: List<SpanBean>) {
    this ?: return
    val ssb = SpannableStringBuilder()
    textList.forEach {
        if (it.text != null) {
            val sbText = SpannableStringBuilder(it.text)
            /**
             * 设置url
             */
            it.url?.first?.let { url ->
                movementMethod = LinkMovementMethod.getInstance()
                sbText.setSpan(object : URLSpan(url) {
                    override fun onClick(widget: View) {
                        super.onClick(widget)
                        val listener = it.url?.second
                        listener?.onClick(widget)
                    }
                }, it)
            }
            /**
             * 设置图片
             */
            it.drawable?.let { drawable ->
                drawable.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight)
                val span = when (it.gravity) {
                    Gravity.TOP -> ImageSpan(drawable, ImageSpan.ALIGN_BASELINE)
                    Gravity.BOTTOM -> ImageSpan(drawable, ImageSpan.ALIGN_BOTTOM)
                    else -> {
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
                            ImageSpan(drawable, ImageSpan.ALIGN_CENTER)
                        else CenterImageSpan(drawable)
                    }
                }
                it.text = "0"
                sbText.clear()
                sbText.append(it.text)
                sbText.setSpan(span, it)
            }
            /**
             * 利用图片控制宽度
             */
            val widthSpace = it.widthSpace
            val heightSpace = it.heightSpace
            if (widthSpace != null || heightSpace != null) {
                val drawable = ColorDrawable()
                drawable.setBounds(0, 0, widthSpace ?: 0, heightSpace ?: 0)
                it.text = "0"
                sbText.clear()
                sbText.append(it.text)
                sbText.setSpan(ImageSpan(drawable), it)
            }
            /**
             * 如果点击不为null，则按照下面方式这是文字颜色和下划线
             */
            if (it.textClickListener != null) {
                it.textClickListener?.let { click ->
                    //这个一定要记得设置，不然点击不生效
                    movementMethod = LinkMovementMethod.getInstance()
                    sbText.setSpan(object : ClickableSpan() {
                        override fun onClick(widget: View) {
                            click.invoke(widget)
                        }

                        override fun updateDrawState(ds: TextPaint) {
                            super.updateDrawState(ds)
                            (it.underlineColor ?: it.textColor)?.let { color ->
                                ds.color = color
                            }
                            it.isUnderlineText?.let {
                                ds.isUnderlineText = it
                            }
                        }
                    }, it)
                }
            } else {
                it.textColor?.let { textColor ->
                    sbText.setSpan(object : ForegroundColorSpan(textColor) {
                        override fun updateDrawState(textPaint: TextPaint) {
                            super.updateDrawState(textPaint)
                            (it.underlineColor ?: it.textColor)?.let { color ->
                                textPaint.color = color
                            }
                            it.isUnderlineText?.let {
                                textPaint.isUnderlineText = it
                            }
                        }
                    }, it)
                }
            }

            /**
             * 文字大小
             */
            it.textSize?.let { textSize ->
                sbText.setSpan(AbsoluteSizeSpan(textSize, true), it)
            }
            /**
             * 文字样式
             */
            it.typeface?.let { typeface ->
                sbText.setSpan(StyleSpan(typeface), it)
            }
            /**
             * 删除线
             */
            if (it.strikethroughSpan == true) {
                sbText.setSpan(StrikethroughSpan(), it)
            }
            /**
             * 背景色
             */
            it.backgroundColor?.let { backgroundColor ->
                sbText.setSpan(BackgroundColorSpan(backgroundColor), it)
            }
            /**
             * 其他span
             */
            it.spanWhatList?.forEach { what ->
                sbText.setSpan(what, it)
            }
            /**
             * 是否加粗
             */
            it.isBold?.let { isBold ->
                sbText.setSpan(object : CharacterStyle() {
                    override fun updateDrawState(p0: TextPaint?) {
                        p0?.isFakeBoldText = isBold
                    }
                }, it)
            }
            /**
             * 行高
             */
            it.lineHeight?.let { lineHeight ->
                sbText.setSpan(TextHeightSpan(lineHeight, it.gravity), it)
            }
            ssb.append(sbText)
        }
    }
    text = ssb
    //想去掉点击后文字背景，设置一下HighlightColor即可
    highlightColor = Color.TRANSPARENT
}

private fun SpannableStringBuilder.setSpan(what: Any, spanBean: SpanBean) {
    val text = spanBean.text ?: return
    setSpan(what, 0, text.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
}

internal class CenterImageSpan : ImageSpan {
    constructor(context: Context, bitmap: Bitmap) : super(context, bitmap)
    constructor(context: Context, bitmap: Bitmap, verticalAlignment: Int) : super(
        context,
        bitmap,
        verticalAlignment
    )

    constructor(drawable: Drawable) : super(drawable)
    constructor(drawable: Drawable, verticalAlignment: Int) : super(drawable, verticalAlignment)
    constructor(drawable: Drawable, source: String) : super(drawable, source)
    constructor(drawable: Drawable, source: String, verticalAlignment: Int) : super(
        drawable,
        source,
        verticalAlignment
    )

    constructor(context: Context, uri: Uri) : super(context, uri)
    constructor(context: Context, uri: Uri, verticalAlignment: Int) : super(
        context,
        uri,
        verticalAlignment
    )

    constructor(context: Context, resourceId: Int) : super(context, resourceId)
    constructor(context: Context, resourceId: Int, verticalAlignment: Int) : super(
        context,
        resourceId,
        verticalAlignment
    )

    override fun draw(
        canvas: Canvas,
        text: CharSequence?,
        start: Int,
        end: Int,
        x: Float,
        top: Int,
        y: Int,
        bottom: Int,
        paint: Paint
    ) {
        val b = drawable
        val fm = paint.fontMetricsInt
        val transY = ((y + fm.descent + y + fm.ascent) / 2
                - b.bounds.bottom / 2)
        canvas.save()
        canvas.translate(x, transY.toFloat())
        b.draw(canvas)
        canvas.restore()
    }
}

/**
 * 文本高度span,针对单行，多行可能有问题
 */
internal class TextHeightSpan(private val height: Int, private val gravity: Int? = null) :
    LineHeightSpan {
    override fun chooseHeight(
        text: CharSequence, start: Int, end: Int,
        spanstartv: Int, lineHeight: Int,
        fm: Paint.FontMetricsInt
    ) {
        val originHeight = fm.descent - fm.ascent
        if (originHeight <= 0) {
            return
        }
        when (gravity) {
            Gravity.TOP -> {
                fm.descent += (height - originHeight)
            }

            Gravity.BOTTOM -> {
                fm.ascent -= (height - originHeight)
            }

            else -> {
                fm.ascent -= height / 2
                fm.descent += (height / 2)
            }
        }
    }
}


/**
 * 毫秒换成00:00:00
 */
//一小时的毫秒数
internal const val ONE_HOUR_OF_MILLISECOND = 1 * 60 * 60 * 1000L

/**
 * 毫秒换成00:00:00
 */
internal fun getCountTimeByLong(
    finishTime: Long,
    isNeedHour: Boolean = finishTime > ONE_HOUR_OF_MILLISECOND,/*大于1小时则显示*/
    splitStrHM: String = ":",/*小时和分钟的分割字符串*/
    splitStrMS: String = ":",/*分钟和秒数的分割字符串*/
): String {
    var totalTime = (finishTime / 1000).toInt()//秒
    var hour = 0
    var minute = 0
    var second = 0
    if (3600 <= totalTime) {
        hour = totalTime / 3600
        totalTime -= 3600 * hour
    }
    if (60 <= totalTime) {
        minute = totalTime / 60
        totalTime -= 60 * minute
    }
    if (0 <= totalTime) {
        second = totalTime
    }
    val sb = StringBuilder()
    if (isNeedHour) {
        if (hour < 10) {
            sb.append("0").append(hour).append(splitStrHM)
        } else {
            sb.append(hour).append(splitStrHM)
        }
    }
    if (minute < 10) {
        sb.append("0").append(minute).append(splitStrMS)
    } else {
        sb.append(minute).append(splitStrMS)
    }
    if (second < 10) {
        sb.append("0").append(second)
    } else {
        sb.append(second)
    }
    return sb.toString()
}

/**
 * 忽略错误
 */
internal inline fun <T> ignoreError(
    errorBlock: ((Throwable) -> T?) = { null },
    finallyBlock: (() -> Unit) = {},
    block: () -> T?
): T? {
    return try {
        block.invoke()
    } catch (e: Throwable) {
        e.printStackTrace()
        errorBlock.invoke(e)
    } finally {
        finallyBlock.invoke()
    }
}

/**
 * 转成pcm文件
 */
@WorkerThread
fun String?.toPcmFile(
    outputFile: File? = null,
    supportMaxSampleRate: Int = 192000
): OutputPcmInfo? {
    this ?: return null
    return File(this).toPcmFile(outputFile, supportMaxSampleRate)
}

/**
 * 转成pcm文件
 */
@WorkerThread
fun File?.toPcmFile(outputFile: File? = null, supportMaxSampleRate: Int = 192000): OutputPcmInfo? {
    this ?: return null
    if (!this.exists()) {
        return null
    }
    if (!this.isFile) {
        return null
    }
    //输入的文件
    val inputPath = this.path
    val infoSession = FFprobeKit.getMediaInformation(inputPath)
    if (!infoSession.isMediaInformation) {
        return null
    }
    val info = inputPath.getMediaInfo() ?: return null
    //输出的文件 eg：xxx.pcm
    val output = outputFile ?: File(this.parent, "${this.name}.pcm")
    val outputDir = output.parentFile ?: return null
    if (!outputDir.exists()) {
        outputDir.mkdirs()
    }
    if (output.exists()) {
        output.delete()
    }
    output.createNewFile()
    //输出路径
    val outputPath = output.path
    //通道
    val ac = info.ac
    //采样率
    val sampleRate = min(info.sampleRate, supportMaxSampleRate)
    //指令
    val command = "-i \"$inputPath\" -y -f s16le -ac $ac -ar $sampleRate \"$outputPath\""
    val session = FFmpegKit.executeAsync(/* command = */ command,
        /* completeCallback = */ {
        },
        /* logCallback = */ {
        },
        /* statisticsCallback = */ {
        }
    )
    return OutputPcmInfo(output, session, info.copy(sampleRate = sampleRate))
}

/**
 * 解码获取音频信息
 */
@WorkerThread
fun String?.getMediaInfo(): MediaInfo? {
    this ?: return null
    val infoSession = FFprobeKit.getMediaInformation(this) ?: return null
    val information = infoSession.mediaInformation ?: return null
    val defaultValue = 0
    var acTemp = defaultValue
    var sampleRateTemp = defaultValue
    information.streams?.forEach {
        if (it.type == "audio") {
            sampleRateTemp = it.sampleRate.toDouble().toInt()
            acTemp = it.getStringProperty("channels").toInt()
        }
    }
    if (acTemp == defaultValue) {
        acTemp = 2
    }
    if (sampleRateTemp == defaultValue) {
        sampleRateTemp = 44100
    }
    val duration = information.duration.ifBlank { "0" }.toDouble().toInt()
    return MediaInfo(this, sampleRateTemp, acTemp, duration)
}

/**
 * 解码信息
 */
data class OutputPcmInfo(
    val file: File,
    val session: FFmpegSession,
    val inputInfo: MediaInfo
)

/**
 * 音频信息
 */
data class MediaInfo(
    val filePath: String,
    val sampleRate: Int,
    val ac: Int,
    val duration: Int,
)

/**
 * 播放pcm文件
 */
fun playPcmAudio(sampleRateInHz: Int, action: (AudioTrack) -> Unit) {
    val bufferSize = AudioTrack.getMinBufferSize(
        sampleRateInHz,  // 采样率
        AudioFormat.CHANNEL_OUT_STEREO,  // 声道配置
        AudioFormat.ENCODING_PCM_16BIT  // PCM编码
    )

    val audioTrack = AudioTrack.Builder()
        .setAudioAttributes(
            AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_MEDIA)
                .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                .build()
        )
        .setAudioFormat(
            AudioFormat.Builder()
                .setSampleRate(sampleRateInHz)
                .setChannelMask(AudioFormat.CHANNEL_OUT_STEREO)
                .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
                .build()
        )
        .setBufferSizeInBytes(bufferSize)
        .setTransferMode(AudioTrack.MODE_STREAM)
        .build()
    audioTrack.play()
    action.invoke(audioTrack)
    audioTrack.stop()
    audioTrack.release()
}

